ํ์ด์ฌ 'email' ํจํค์ง์ ๋ชจ๋ ๊ฒ. ๋ณต์กํ MIME ๋ฉ์์ง๋ฅผ ๊ตฌ์ฑํ๊ณ ์์ ์ด๋ฉ์ผ์ ํจ๊ณผ์ ์ด๊ณ ์ ์ญ์ ์ผ๋ก ํ์ฑํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์ถ์ถํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์ฐ์ธ์.
ํ์ด์ฌ email ํจํค์ง ๋ง์คํฐํ๊ธฐ: MIME ๋ฉ์์ง ๊ตฌ์ฑ๊ณผ ๊ฐ๋ ฅํ ํ์ฑ์ ๊ธฐ์
์ด๋ฉ์ผ์ ๊ฐ์ธ์ ์ธ ์์ , ๋น์ฆ๋์ค ์ด์, ์๋ํ๋ ์์คํ ์๋ฆผ์ ํ์์ ์ธ ๊ธ๋ก๋ฒ ์ปค๋ฎค๋์ผ์ด์ ์ ์ด์์ผ๋ก ๋จ์์์ต๋๋ค. ๋ชจ๋ ๋ฆฌ์น ํ ์คํธ ์ด๋ฉ์ผ, ๋ชจ๋ ์ฒจ๋ถํ์ผ, ๊ทธ๋ฆฌ๊ณ ์ธ์ฌํ๊ฒ ํ์ํ๋ ๋ชจ๋ ์๋ช ๋ค์๋ MIME(Multipurpose Internet Mail Extensions)์ ๋ณต์ก์ฑ์ด ์จ์ด ์์ต๋๋ค. ๊ฐ๋ฐ์, ํนํ ํ์ด์ฌ์ผ๋ก ์์ ํ๋ ๊ฐ๋ฐ์์๊ฒ ์ด๋ฌํ MIME ๋ฉ์์ง๋ฅผ ํ๋ก๊ทธ๋๋ฐ ๋ฐฉ์์ผ๋ก ๊ตฌ์ฑํ๊ณ ํ์ฑํ๋ ๋ฐฉ๋ฒ์ ๋ง์คํฐํ๋ ๊ฒ์ ์ค์ํ ๊ธฐ์ ์ ๋๋ค.
ํ์ด์ฌ์ ๋ด์ฅ email
ํจํค์ง๋ ์ด๋ฉ์ผ ๋ฉ์์ง๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํ ๊ฐ๋ ฅํ๊ณ ํฌ๊ด์ ์ธ ํ๋ ์์ํฌ๋ฅผ ์ ๊ณตํฉ๋๋ค. ์ด๊ฒ์ ๋จ์ํ ๊ฐ๋จํ ํ
์คํธ๋ฅผ ๋ณด๋ด๋ ๊ฒ๋ฟ๋ง ์๋๋ผ, MIME์ ๋ณต์กํ ์ธ๋ถ ์ฌํญ์ ์ถ์ํํ์ฌ ์ ๊ตํ ์ด๋ฉ์ผ์ ์์ฑํ๊ณ ์์ ์ด๋ฉ์ผ์์ ํน์ ๋ฐ์ดํฐ๋ฅผ ๋๋ผ์ด ์ ๋ฐ๋๋ก ์ถ์ถํ ์ ์๋๋ก ์ค๊ณ๋์์ต๋๋ค. ์ด ๊ฐ์ด๋๋ ์ด ํจํค์ง์ ๋ ๊ฐ์ง ์ฃผ์ ์ธก๋ฉด์ธ ๋ฐ์ก์ ์ํ MIME ๋ฉ์์ง ๊ตฌ์ฑ๊ณผ ๋ฐ์ดํฐ ์ถ์ถ์ ์ํ ํ์ฑ์ ๋ํด ๊น์ด ํ๊ณ ๋ค๋ฉฐ, ๋ชจ๋ฒ ์ฌ๋ก์ ๋ํ ๊ธ๋ก๋ฒ ๊ด์ ์ ์ ๊ณตํ ๊ฒ์
๋๋ค.
๊ตฌ์ฑ์ ํ์ฑ์ ๋ชจ๋ ์ดํดํ๋ ๊ฒ์ ๋งค์ฐ ์ค์ํฉ๋๋ค. ๋ฉ์์ง๋ฅผ ๊ตฌ์ฑํ ๋, ๋น์ ์ ๋ณธ์ง์ ์ผ๋ก ๋ค๋ฅธ ์์คํ ์ด ํด์ํ ๊ตฌ์กฐ์ ์ฝํ ์ธ ๋ฅผ ์ ์ํ๋ ๊ฒ์ ๋๋ค. ํ์ฑํ ๋๋ ๋ค๋ฅธ ์์คํ ์ ์ํด ์ ์๋ ๊ตฌ์กฐ์ ์ฝํ ์ธ ๋ฅผ ํด์ํ๋ ๊ฒ์ ๋๋ค. ํ๋์ ๋ํ ๊น์ ์ดํด๋ ๋ค๋ฅธ ํ๋๋ฅผ ๋ง์คํฐํ๋ ๋ฐ ํฐ ๋์์ด ๋๋ฉฐ, ์ด๋ ๋ ํ๋ ฅ์ ์ด๊ณ ์ํธ ์ด์ฉ ๊ฐ๋ฅํ ์ด๋ฉ์ผ ์ ํ๋ฆฌ์ผ์ด์ ์ผ๋ก ์ด์ด์ง๋๋ค.
MIME ์ดํดํ๊ธฐ: ํ๋ ์ด๋ฉ์ผ์ ๊ทผ๊ฐ
ํ์ด์ฌ์ ๊ตฌ์ฒด์ ์ธ ๋ด์ฉ์ ๋ค์ด๊ฐ๊ธฐ ์ ์, MIME์ด ๋ฌด์์ด๋ฉฐ ์ ๊ทธ๋ ๊ฒ ์ค์ํ์ง๋ฅผ ํ์ ํ๋ ๊ฒ์ด ํ์์ ์ ๋๋ค. ์๋ ์ด๋ฉ์ผ ๋ฉ์์ง๋ ์ผ๋ฐ ํ ์คํธ(7๋นํธ ASCII ๋ฌธ์)๋ก ์ ํ๋์์ต๋๋ค. 1990๋ ๋ ์ด์ ๋์ ๋ MIME์ ์ด๋ฉ์ผ์ ๊ธฐ๋ฅ์ ๋ค์๊ณผ ๊ฐ์ด ํ์ฅํ์ต๋๋ค:
- ๋น-ASCII ๋ฌธ์: ์๋์ด, ์ค๊ตญ์ด, ๋ฌ์์์ด ๋ฑ ASCII ์ธํธ ๋ฐ์ ๋ฌธ์๋ฅผ ์ฌ์ฉํ๋ ์ธ์ด๋ก ํ ์คํธ๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
- ์ฒจ๋ถํ์ผ: ๋ฌธ์, ์ด๋ฏธ์ง, ์ค๋์ค, ๋น๋์ค์ ๊ฐ์ ํ์ผ์ ๋ณด๋ผ ์ ์์ต๋๋ค.
- ๋ฆฌ์น ํ ์คํธ ์์: ๊ตต๊ฒ, ๊ธฐ์ธ์๊ผด, ์์ ๋ฐ ๋ ์ด์์์ด ์๋ HTML ์ด๋ฉ์ผ.
- ๋ค์ค ํํธ: ์ผ๋ฐ ํ ์คํธ, HTML ๋ฐ ์ฒจ๋ถํ์ผ์ ๋จ์ผ ์ด๋ฉ์ผ ๋ด์ ๊ฒฐํฉ.
MIME์ ์ด๋ฉ์ผ ๋ฉ์์ง์ ํน์ ํค๋๋ฅผ ์ถ๊ฐํ๊ณ ๋ณธ๋ฌธ์ ๋ค์ํ "ํํธ"๋ก ๊ตฌ์กฐํํจ์ผ๋ก์จ ์ด๋ฅผ ๋ฌ์ฑํฉ๋๋ค. ์์ฃผ ์ ํ๊ฒ ๋ ์ฃผ์ MIME ํค๋๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
Content-Type:
ํํธ์ ๋ฐ์ดํฐ ์ ํ์ ์ง์ ํฉ๋๋ค (์:text/plain
,text/html
,image/jpeg
,application/pdf
,multipart/alternative
). ์ข ์ขcharset
๋งค๊ฐ๋ณ์๋ ํฌํจํฉ๋๋ค (์:charset=utf-8
).Content-Transfer-Encoding:
์ด๋ฉ์ผ ํด๋ผ์ด์ธํธ๊ฐ ์ฝํ ์ธ ๋ฅผ ์ด๋ป๊ฒ ๋์ฝ๋ฉํด์ผ ํ๋์ง๋ฅผ ๋ํ๋ ๋๋ค (์: ์ด์ง ๋ฐ์ดํฐ์ ๊ฒฝ์ฐbase64
, ๋น-ASCII ๋ฌธ์๊ฐ ์ผ๋ถ ํฌํจ๋ ๋๋ถ๋ถ์ ํ ์คํธ์ ๊ฒฝ์ฐquoted-printable
).Content-Disposition:
์์ ์์ ์ด๋ฉ์ผ ํด๋ผ์ด์ธํธ๊ฐ ํํธ๋ฅผ ์ด๋ป๊ฒ ํ์ํด์ผ ํ๋์ง๋ฅผ ์ ์ํฉ๋๋ค (์: ๋ฉ์์ง ๋ณธ๋ฌธ ๋ด์ ํ์ํ ๊ฒฝ์ฐinline
, ์ ์ฅํ ํ์ผ์ ๊ฒฝ์ฐattachment
).
ํ์ด์ฌ email
ํจํค์ง: ์ฌ์ธต ๋ถ์
ํ์ด์ฌ์ email
ํจํค์ง๋ ํ๋ก๊ทธ๋๋ฐ ๋ฐฉ์์ผ๋ก ์ด๋ฉ์ผ ๋ฉ์์ง๋ฅผ ์์ฑ, ํ์ฑ ๋ฐ ์์ ํ๊ธฐ ์ํด ์ค๊ณ๋ ํฌ๊ด์ ์ธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์
๋๋ค. ์ด๋ฉ์ผ์ ๊ตฌ์กฐ๋ฅผ ๋ํ๋ด๋ Message
๊ฐ์ฒด ๊ฐ๋
์ ์ค์ฌ์ผ๋ก ๊ตฌ์ถ๋์์ต๋๋ค.
ํจํค์ง ๋ด์ ์ฃผ์ ๋ชจ๋์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
email.message:
์ด๋ฉ์ผ ๋ฉ์์ง๋ฅผ ์์ฑํ๊ณ ์กฐ์ํ๊ธฐ ์ํ ๊ธฐ๋ณธ ์ธํฐํ์ด์ค์ธ ํต์ฌEmailMessage
ํด๋์ค๋ฅผ ํฌํจํฉ๋๋ค. MIME ์ธ๋ถ ์ ๋ณด๋ฅผ ์๋์ผ๋ก ์ฒ๋ฆฌํ๋ ๋งค์ฐ ์ ์ฐํ ํด๋์ค์ ๋๋ค.email.mime:
MIME ๊ตฌ์กฐ์ ๋ํด ๋ ๋ช ์์ ์ธ ์ ์ด๋ฅผ ์ ๊ณตํ๋ ๋ ๊ฑฐ์ ํด๋์ค(MIMEText
,MIMEMultipart
๋ฑ)๋ฅผ ์ ๊ณตํฉ๋๋ค. ๋จ์์ฑ ๋๋ฌธ์ ์๋ก์ด ์ฝ๋์๋ ์ผ๋ฐ์ ์ผ๋กEmailMessage
๊ฐ ์ ํธ๋์ง๋ง, ์ด๋ฌํ ํด๋์ค๋ฅผ ์ดํดํ๋ ๊ฒ์ด ๋์์ด ๋ ์ ์์ต๋๋ค.email.parser:
์์ ์ด๋ฉ์ผ ๋ฐ์ดํฐ(๋ฐ์ดํธ ๋๋ ๋ฌธ์์ด)๋ฅผEmailMessage
๊ฐ์ฒด๋ก ๋ณํํ๋BytesParser
๋ฐParser
์ ๊ฐ์ ํด๋์ค๋ฅผ ์ ๊ณตํฉ๋๋ค.email.policy:
ํค๋ ์ธ์ฝ๋ฉ, ์ค ๋, ์ค๋ฅ ์ฒ๋ฆฌ์ ์ํฅ์ ๋ฏธ์น๋ ์ด๋ฉ์ผ ๋ฉ์์ง ๊ตฌ์ฑ ๋ฐ ํ์ฑ ๋ฐฉ๋ฒ์ ์ ์ดํ๋ ์ ์ฑ ์ ์ ์ํฉ๋๋ค.
๋๋ถ๋ถ์ ํ๋์ ์ธ ์ฌ์ฉ ์ฌ๋ก์์๋ ๊ตฌ์ฑ ๋ฐ ํ์ฑ๋ ๋ฉ์์ง ๊ฐ์ฒด ๋ชจ๋์ ๋ํด ์ฃผ๋ก email.message.EmailMessage
ํด๋์ค์ ์ํธ ์์ฉํ๊ฒ ๋ ๊ฒ์
๋๋ค. ์ด ํด๋์ค์ ๋ฉ์๋๋ ๋ ๊ฑฐ์ email.mime
ํด๋์ค๋ฅผ ์ฌ์ฉํ๋ ๋ ์ฅํฉํ ํ๋ก์ธ์ค๋ฅผ ํฌ๊ฒ ๋จ์ํํฉ๋๋ค.
MIME ๋ฉ์์ง ๊ตฌ์ฑ: ์ ๋ฐํ๊ฒ ์ด๋ฉ์ผ ๋ง๋ค๊ธฐ
์ด๋ฉ์ผ ๊ตฌ์ฑ์ ๋ค์ํ ๊ตฌ์ฑ ์์(ํ
์คํธ, HTML, ์ฒจ๋ถํ์ผ)๋ฅผ ์ ํจํ MIME ๊ตฌ์กฐ๋ก ์กฐ๋ฆฝํ๋ ๊ฒ์ ํฌํจํฉ๋๋ค. EmailMessage
ํด๋์ค๋ ์ด ํ๋ก์ธ์ค๋ฅผ ์๋นํ ๊ฐ์ํํฉ๋๋ค.
๊ธฐ๋ณธ ํ ์คํธ ์ด๋ฉ์ผ
๊ฐ์ฅ ๊ฐ๋จํ ์ด๋ฉ์ผ์ ์ผ๋ฐ ํ ์คํธ์ ๋๋ค. ์์ฝ๊ฒ ํ๋๋ฅผ ๋ง๋ค๊ณ ๊ธฐ๋ณธ ํค๋๋ฅผ ์ค์ ํ ์ ์์ต๋๋ค:
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'Greetings from Python'
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'
msg.set_content('Hello, this is a plain text email sent from Python.\n\nBest regards,\nYour Python Script')
print(msg.as_string())
์ค๋ช :
EmailMessage()
๋ ๋น ๋ฉ์์ง ๊ฐ์ฒด๋ฅผ ์์ฑํฉ๋๋ค.- ์ฌ์ ๊ณผ ์ ์ฌํ ์ ๊ทผ(
msg['Subject'] = ...
)์ผ๋ก ๊ณตํต ํค๋๋ฅผ ์ค์ ํฉ๋๋ค. set_content()
๋ ์ด๋ฉ์ผ์ ์ฃผ์ ์ฝํ ์ธ ๋ฅผ ์ถ๊ฐํฉ๋๋ค. ๊ธฐ๋ณธ์ ์ผ๋กContent-Type: text/plain; charset="utf-8"
์ ์ถ๋ก ํฉ๋๋ค.as_string()
์ ๋ฉ์์ง๋ฅผ SMTP๋ฅผ ํตํด ๋ณด๋ด๊ฑฐ๋ ํ์ผ์ ์ ์ฅํ๊ธฐ์ ์ ํฉํ ๋ฌธ์์ด ํ์์ผ๋ก ์ง๋ ฌํํฉ๋๋ค.
HTML ์ฝํ ์ธ ์ถ๊ฐํ๊ธฐ
HTML ์ด๋ฉ์ผ์ ๋ณด๋ด๋ ค๋ฉด set_content()
๋ฅผ ํธ์ถํ ๋ ์ฝํ
์ธ ์ ํ์ ์ง์ ํ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค. HTML์ ๋ ๋๋งํ์ง ์๋ ์ด๋ฉ์ผ ํด๋ผ์ด์ธํธ๋ฅผ ๊ฐ์ง ์์ ์๋ ์ ๊ทผ์ฑ์ ์ํด ์ผ๋ฐ ํ
์คํธ ๋์์ ์ ๊ณตํ๋ ๊ฒ์ด ์ข์ ์ต๊ด์
๋๋ค.
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'Your HTML Newsletter'
msg['From'] = 'newsletter@example.com'
msg['To'] = 'subscriber@example.com'
html_content = """
<html>
<head></head>
<body>
<h1>Welcome to Our Global Update!</h1>
<p>Dear Subscriber,</p>
<p>This is your <strong>latest update</strong> from around the world.</p>
<p>Visit our <a href="http://www.example.com">website</a> for more.</p>
<p>Best regards,<br>The Team</p>
</body>
</html>
"""
# HTML ๋ฒ์ ์ถ๊ฐ
msg.add_alternative(html_content, subtype='html')
# ์ผ๋ฐ ํ
์คํธ ๋์ฒด ์ฝํ
์ธ ์ถ๊ฐ
plain_text_content = (
"Welcome to Our Global Update!\n\n"
"Dear Subscriber,\n\n"
"This is your latest update from around the world.\n"
"Visit our website for more: http://www.example.com\n\n"
"Best regards,\nThe Team"
)
msg.add_alternative(plain_text_content, subtype='plain')
print(msg.as_string())
์ค๋ช :
add_alternative()
๋ *๋์ผํ* ์ฝํ ์ธ ์ ๋ค๋ฅธ ํํ์ ์ถ๊ฐํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค. ์ด๋ฉ์ผ ํด๋ผ์ด์ธํธ๋ ์ฒ๋ฆฌํ ์ ์๋ "์ต์์" ๋ฒ์ ์ ํ์ํฉ๋๋ค (๋ณดํต HTML).- ์ด๋ ์๋์ผ๋ก
multipart/alternative
MIME ๊ตฌ์กฐ๋ฅผ ์์ฑํฉ๋๋ค.
์ฒจ๋ถํ์ผ ์ฒ๋ฆฌํ๊ธฐ
ํ์ผ ์ฒจ๋ถ๋ add_attachment()
๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ๋จํ๊ฒ ํ ์ ์์ต๋๋ค. ๋ชจ๋ ์ ํ์ ํ์ผ์ ์ฒจ๋ถํ ์ ์์ผ๋ฉฐ, ํจํค์ง๋ ์ ์ ํ MIME ์ ํ๊ณผ ์ธ์ฝ๋ฉ(๋ณดํต base64
)์ ์ฒ๋ฆฌํฉ๋๋ค.
from email.message import EmailMessage
from pathlib import Path
# ์์ฐ์ ์ํ ๋๋ฏธ ํ์ผ ์์ฑ
Path('report.pdf').write_bytes(b'%PDF-1.4\n1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj\n2 0 obj<</Count 0>>endobj\nxref\n0 3\n0000000000 65535 f\n0000000009 00000 n\n0000000052 00000 n\ntrailer<</Size 3/Root 1 0 R>>startxref\n104\n%%EOF') # ๋งค์ฐ ๊ธฐ๋ณธ์ ์ธ, ์ ํจํ์ง ์์ PDF ํ๋ ์ด์คํ๋
Path('logo.png').write_bytes(b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\x0cIDAT\x08\x99c`\x00\x00\x00\x02\x00\x01\xe2!\x00\xa0\x00\x00\x00\x00IEND\xaeB`\x82') # 1x1 ํฌ๋ช
PNG ํ๋ ์ด์คํ๋
msg = EmailMessage()
msg['Subject'] = 'Important Document and Image'
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'
msg.set_content('Please find the attached report and company logo.')
# PDF ํ์ผ ์ฒจ๋ถ
with open('report.pdf', 'rb') as f:
file_data = f.read()
msg.add_attachment(
file_data,
maintype='application',
subtype='pdf',
filename='Annual_Report_2024.pdf'
)
# ์ด๋ฏธ์ง ํ์ผ ์ฒจ๋ถ
with open('logo.png', 'rb') as f:
image_data = f.read()
msg.add_attachment(
image_data,
maintype='image',
subtype='png',
filename='CompanyLogo.png'
)
print(msg.as_string())
# ๋๋ฏธ ํ์ผ ์ ๋ฆฌ
Path('report.pdf').unlink()
Path('logo.png').unlink()
์ค๋ช :
add_attachment()
๋ ํ์ผ ์ฝํ ์ธ ์ ์์ ๋ฐ์ดํธ๋ฅผ ๋ฐ์ต๋๋ค.maintype
๊ณผsubtype
์ MIME ์ ํ์ ์ง์ ํฉ๋๋ค (์:application/pdf
,image/png
). ์ด๋ ์์ ์์ ์ด๋ฉ์ผ ํด๋ผ์ด์ธํธ๊ฐ ์ฒจ๋ถํ์ผ์ ์ฌ๋ฐ๋ฅด๊ฒ ์๋ณํ๊ณ ์ฒ๋ฆฌํ๋ ๋ฐ ์ค์ํฉ๋๋ค.filename
์ ์์ ์๊ฐ ์ฒจ๋ถํ์ผ์ ์ ์ฅํ ๋ ์ฌ์ฉ๋ ์ด๋ฆ์ ์ ๊ณตํฉ๋๋ค.- ์ด๋ ์๋์ผ๋ก
multipart/mixed
๊ตฌ์กฐ๋ฅผ ์ค์ ํฉ๋๋ค.
๋ฉํฐํํธ ๋ฉ์์ง ์์ฑํ๊ธฐ
HTML ๋ณธ๋ฌธ, ์ผ๋ฐ ํ
์คํธ ๋์ฒด ์ฝํ
์ธ , ๊ทธ๋ฆฌ๊ณ ์ธ๋ผ์ธ ์ด๋ฏธ์ง๋ ๊ด๋ จ ํ์ผ์ด ๋ชจ๋ ์๋ ๋ฉ์์ง๋ฅผ ๋ง๋ค๋ ค๋ฉด ๋ ๋ณต์กํ ๋ฉํฐํํธ ๊ตฌ์กฐ๊ฐ ํ์ํฉ๋๋ค. EmailMessage
ํด๋์ค๋ add_related()
์ add_alternative()
๋ฅผ ์ฌ์ฉํ์ฌ ์ด๋ฅผ ์ง๋ฅ์ ์ผ๋ก ์ฒ๋ฆฌํฉ๋๋ค.
์ผ๋ฐ์ ์ธ ์๋๋ฆฌ์ค๋ HTML ๋ด์ ์ง์ ์ด๋ฏธ์ง๊ฐ ํฌํจ๋ HTML ์ด๋ฉ์ผ("์ธ๋ผ์ธ" ์ด๋ฏธ์ง)์
๋๋ค. ์ด๋ multipart/related
๋ฅผ ์ฌ์ฉํฉ๋๋ค.
from email.message import EmailMessage
from pathlib import Path
# ์์ฐ์ ์ํ ๋๋ฏธ ์ด๋ฏธ์ง ํ์ผ ์์ฑ (1x1 ํฌ๋ช
PNG)
Path('banner.png').write_bytes(b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\x0cIDAT\x08\x99c`\x00\x00\x00\x02\x00\x01\xe2!\x00\xa0\x00\x00\x00\x00IEND\xaeB`\x82')
msg = EmailMessage()
msg['Subject'] = 'Inline Image Example'
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'
# ์ผ๋ฐ ํ
์คํธ ๋ฒ์ (๋์ฒด ์ฝํ
์ธ )
plain_text = 'Check out our amazing banner!\n\n[Image: Banner.png]\n\nVisit our site.'
msg.set_content(plain_text, subtype='plain') # ์ด๊ธฐ ์ผ๋ฐ ํ
์คํธ ์ฝํ
์ธ ์ค์
# HTML ๋ฒ์ (์ธ๋ผ์ธ ์ด๋ฏธ์ง๋ฅผ ์ํ CID ํฌํจ)
html_content = """
<html>
<head></head>
<body>
<h1>Our Latest Offer!</h1>
<p>Dear Customer,</p>
<p>Don't miss out on our special global promotion:</p>
<img src="cid:my-banner-image" alt="Promotion Banner">
<p>Click <a href="http://www.example.com">here</a> to learn more.</p>
</body>
</html>
"""
msg.add_alternative(html_content, subtype='html') # HTML ๋์ฒด ์ฝํ
์ธ ์ถ๊ฐ
# ์ธ๋ผ์ธ ์ด๋ฏธ์ง ์ถ๊ฐ (๊ด๋ จ ์ฝํ
์ธ )
with open('banner.png', 'rb') as img_file:
image_data = img_file.read()
msg.add_related(
image_data,
maintype='image',
subtype='png',
cid='my-banner-image' # ์ด CID๋ HTML์ 'src'์ ์ผ์นํจ
)
print(msg.as_string())
# ๋๋ฏธ ํ์ผ ์ ๋ฆฌ
Path('banner.png').unlink()
์ค๋ช :
set_content()
๋ ์ด๊ธฐ ์ฝํ ์ธ (์ฌ๊ธฐ์๋ ์ผ๋ฐ ํ ์คํธ)๋ฅผ ์ค์ ํฉ๋๋ค.add_alternative()
๋ HTML ๋ฒ์ ์ ์ถ๊ฐํ์ฌ ์ผ๋ฐ ํ ์คํธ์ HTML ํํธ๋ฅผ ํฌํจํ๋multipart/alternative
๊ตฌ์กฐ๋ฅผ ์์ฑํฉ๋๋ค.add_related()
๋ ๋ฉ์์ง ํํธ ์ค ํ๋์ "๊ด๋ จ๋" ์ฝํ ์ธ , ์ผ๋ฐ์ ์ผ๋ก HTML์ ์ธ๋ผ์ธ ์ด๋ฏธ์ง์ ์ฌ์ฉ๋ฉ๋๋ค.cid
(Content-ID) ๋งค๊ฐ๋ณ์๋ฅผ ๋ฐ์ผ๋ฉฐ, ์ด๋ HTML<img src="cid:my-banner-image">
ํ๊ทธ์์ ์ฐธ์กฐ๋ฉ๋๋ค.- ์ต์ข
๊ตฌ์กฐ๋
multipart/alternative
ํํธ๋ฅผ ํฌํจํ๋multipart/mixed
(์ธ๋ถ ์ฒจ๋ถํ์ผ์ด ์๋ ๊ฒฝ์ฐ)๊ฐ ๋๊ณ , ์ดmultipart/alternative
ํํธ๋ ๋ค์multipart/related
ํํธ๋ฅผ ํฌํจํ๊ฒ ๋ฉ๋๋ค.multipart/related
ํํธ๋ HTML๊ณผ ์ธ๋ผ์ธ ์ด๋ฏธ์ง๋ฅผ ํฌํจํฉ๋๋ค.EmailMessage
ํด๋์ค๊ฐ ์ด ์ค์ฒฉ ๋ณต์ก์ฑ์ ์์์ ์ฒ๋ฆฌํด ์ค๋๋ค.
๊ธ๋ก๋ฒ ํต์ ์ ์ํ ์ธ์ฝ๋ฉ ๋ฐ ๋ฌธ์ ์งํฉ
๊ตญ์ ์ ์ธ ์ปค๋ฎค๋์ผ์ด์
์ ์ํด์๋ ์ ์ ํ ๋ฌธ์ ์ธ์ฝ๋ฉ์ด ๊ฐ์ฅ ์ค์ํฉ๋๋ค. email
ํจํค์ง๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ ์ธ๊ณ์ ๋ค์ํ ๋ฌธ์ ์งํฉ์ ์ฒ๋ฆฌํ๋ ๋ณดํธ์ ์ธ ํ์ค์ธ UTF-8์ ์ฌ์ฉํ๋๋ก ๊ฐ๋ ฅํ๊ฒ ๊ถ์ฅํฉ๋๋ค.
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'Global Characters: ใใใซใกใฏ, ะัะธะฒะตั, เคจเคฎเคธเฅเคคเฅ'
msg['From'] = 'global_sender@example.com'
msg['To'] = 'global_recipient@example.com'
# ์ผ๋ณธ์ด, ๋ฌ์์์ด, ํ๋์ด ๋ฌธ์
content = "This message contains diverse global characters:\n"
content += "ใใใซใกใฏ (Japanese)\n"
content += "ะัะธะฒะตั (Russian)\n"
content += "เคจเคฎเคธเฅเคคเฅ (Hindi)\n\n"
content += "The 'email' package handles UTF-8 gracefully."
msg.set_content(content)
print(msg.as_string())
์ค๋ช :
set_content()
๊ฐ ํ์ด์ฌ ๋ฌธ์์ด์ ๋ฐ์ผ๋ฉด, ์๋์ผ๋ก UTF-8๋ก ์ธ์ฝ๋ฉํ๊ณContent-Type: text/plain; charset="utf-8"
ํค๋๋ฅผ ์ค์ ํฉ๋๋ค.- ์ฝํ
์ธ ์ ํ์ํ๋ค๋ฉด(์: ๋น-ASCII ๋ฌธ์๊ฐ ๋ง์ ๊ฒฝ์ฐ), ์ค๋๋ ์ด๋ฉ์ผ ์์คํ
์ ํตํ ์์ ํ ์ ์ก์ ๋ณด์ฅํ๊ธฐ ์ํด
Content-Transfer-Encoding: quoted-printable
๋๋base64
๋ฅผ ์ ์ฉํ ์๋ ์์ต๋๋ค. ํจํค์ง๋ ์ ํ๋ ์ ์ฑ ์ ๋ฐ๋ผ ์ด๋ฅผ ์๋์ผ๋ก ์ฒ๋ฆฌํฉ๋๋ค.
์ฌ์ฉ์ ์ง์ ํค๋ ๋ฐ ์ ์ฑ
์ด๋ฉ์ผ์ ์ด๋ค ์ฌ์ฉ์ ์ง์ ํค๋๋ ์ถ๊ฐํ ์ ์์ต๋๋ค. ์ ์ฑ
(email.policy
์์ ๊ฐ์ ธ์ด)์ ๋ฉ์์ง๊ฐ ์ฒ๋ฆฌ๋๋ ๋ฐฉ์์ ์ ์ํ๋ฉฐ, ํค๋ ์ธ์ฝ๋ฉ, ์ค ๋, ์ค๋ฅ ์ฒ๋ฆฌ์ ๊ฐ์ ์ธก๋ฉด์ ์ํฅ์ ๋ฏธ์นฉ๋๋ค. ๊ธฐ๋ณธ ์ ์ฑ
์ ์ผ๋ฐ์ ์ผ๋ก ์ข์ง๋ง, ์๊ฒฉํ SMTP ์ค์๋ฅผ ์ํด `SMTP`๋ฅผ ์ ํํ๊ฑฐ๋ ์ฌ์ฉ์ ์ง์ ์ ์ฑ
์ ์ ์ํ ์ ์์ต๋๋ค.
from email.message import EmailMessage
from email import policy
msg = EmailMessage(policy=policy.SMTP)
msg['Subject'] = 'Email with Custom Header'
msg['From'] = 'info@example.org'
msg['To'] = 'user@example.org'
msg['X-Custom-Header'] = 'This is a custom value for tracking'
msg['Reply-To'] = 'support@example.org'
msg.set_content('This email demonstrates custom headers and policies.')
print(msg.as_string())
์ค๋ช :
policy=policy.SMTP
๋ฅผ ์ฌ์ฉํ๋ฉด SMTP ํ์ค์ ์๊ฒฉํ๊ฒ ์ค์ํ๊ฒ ๋์ด ์ ๋ฌ ๊ฐ๋ฅ์ฑ์ ๋งค์ฐ ์ค์ํ ์ ์์ต๋๋ค.- ์ฌ์ฉ์ ์ง์ ํค๋๋ ํ์ค ํค๋์ ๋์ผํ๊ฒ ์ถ๊ฐ๋ฉ๋๋ค. ๋นํ์ค ํค๋๋ฅผ ๋ํ๋ด๊ธฐ ์ํด ์ข
์ข
X-
๋ก ์์ํฉ๋๋ค.
MIME ๋ฉ์์ง ํ์ฑ: ์์ ์ด๋ฉ์ผ์์ ์ ๋ณด ์ถ์ถํ๊ธฐ
ํ์ฑ์ ์์ ์ด๋ฉ์ผ ๋ฐ์ดํฐ(์ผ๋ฐ์ ์ผ๋ก IMAP์ ํตํด ๋ฐ๊ฑฐ๋ ํ์ผ์์ ์ฝ์)๋ฅผ ๊ฐ์ ธ์ ์ฝ๊ฒ ๊ฒ์ฌํ๊ณ ์กฐ์ํ ์ ์๋ `EmailMessage` ๊ฐ์ฒด๋ก ๋ณํํ๋ ๊ณผ์ ์ ํฌํจํฉ๋๋ค.
๋ก๋ ๋ฐ ์ด๊ธฐ ํ์ฑ
์ผ๋ฐ์ ์ผ๋ก ์ด๋ฉ์ผ์ ์์ ๋ฐ์ดํธ๋ก ๋ฐ๊ฒ ๋ฉ๋๋ค. ์ด๋ฅผ ์ํด email.parser.BytesParser
(๋๋ ํธ์ ํจ์์ธ email.message_from_bytes()
)๊ฐ ์ฌ์ฉ๋ฉ๋๋ค.
from email.parser import BytesParser
from email.policy import default
raw_email_bytes = b"""
From: sender@example.com
To: recipient@example.com
Subject: Test Email with Basic Headers
Date: Mon, 1 Jan 2024 10:00:00 +0000
Content-Type: text/plain; charset="utf-8"
This is the body of the email.
It's a simple test.
"""
# BytesParser ์ฌ์ฉ
parser = BytesParser(policy=default)
msg = parser.parsebytes(raw_email_bytes)
# ๋๋ ํธ์ ํจ์ ์ฌ์ฉ
# from email import message_from_bytes
# msg = message_from_bytes(raw_email_bytes, policy=default)
print(f"Subject: {msg['subject']}")
print(f"From: {msg['from']}")
print(f"Content-Type: {msg['Content-Type']}")
์ค๋ช :
BytesParser
๋ ์์ ๋ฐ์ดํธ ๋ฐ์ดํฐ(์ด๋ฉ์ผ์ด ์ ์ก๋๋ ๋ฐฉ์)๋ฅผ ๋ฐ์EmailMessage
๊ฐ์ฒด๋ฅผ ๋ฐํํฉ๋๋ค.policy=default
๋ ํ์ฑ ๊ท์น์ ์ง์ ํฉ๋๋ค.
ํค๋ ์ ๊ทผํ๊ธฐ
ํค๋๋ ์ฌ์ ๊ณผ ์ ์ฌํ ํค๋ฅผ ํตํด ์ฝ๊ฒ ์ ๊ทผํ ์ ์์ต๋๋ค. ํจํค์ง๋ ์ธ์ฝ๋ฉ๋ ํค๋(์: ๊ตญ์ ๋ฌธ์๊ฐ ํฌํจ๋ ์ ๋ชฉ)์ ๋์ฝ๋ฉ์ ์๋์ผ๋ก ์ฒ๋ฆฌํฉ๋๋ค.
# ... (์ด์ ํ์ฑ ์์ ์ 'msg' ๊ฐ์ฒด ์ฌ์ฉ)
print(f"Date: {msg['date']}")
print(f"Message ID: {msg['Message-ID'] if 'Message-ID' in msg else 'N/A'}")
# ์ฌ๋ฌ ํค๋ ์ฒ๋ฆฌ (์: 'Received' ํค๋)
# from email.message import EmailMessage # ์์ง importํ์ง ์์๋ค๋ฉด
# from email import message_from_string # ๊ฐ๋จํ ๋ฌธ์์ด ์์ ๋ฅผ ์ํด
multi_header_email = message_from_string(
"""
From: a@example.com
To: b@example.com
Subject: Multi-header Test
Received: from client.example.com (client.example.com [192.168.1.100])
by server.example.com (Postfix) with ESMTP id 123456789
for <b@example.com>; Mon, 1 Jan 2024 10:00:00 +0000 (GMT)
Received: from mx.another.com (mx.another.com [192.168.1.101])
by server.example.com (Postfix) with ESMTP id 987654321
for <b@example.com>; Mon, 1 Jan 2024 09:59:00 +0000 (GMT)
Body content here.
"""
)
received_headers = multi_header_email.get_all('received')
if received_headers:
print("\nReceived Headers:")
for header in received_headers:
print(f"- {header}")
์ค๋ช :
- ํค๋์ ์ ๊ทผํ๋ฉด ๊ทธ ๊ฐ์ ๋ฌธ์์ด๋ก ๋ฐํํฉ๋๋ค.
get_all('header-name')
์ ์ฌ๋ฌ ๋ฒ ๋ํ๋ ์ ์๋ ํค๋(์:Received
)์ ์ ์ฉํฉ๋๋ค.- ํจํค์ง๋ ํค๋ ๋์ฝ๋ฉ์ ์ฒ๋ฆฌํ๋ฏ๋ก
Subject: =?utf-8?Q?Global_Characters:_=E3=81=93=E3=82=93=E3=81=AB=E3=81=A1=E3=81=AF?=
์ ๊ฐ์ ๊ฐ์ ์๋์ผ๋ก ์ฝ์ ์ ์๋ ๋ฌธ์์ด๋ก ๋ณํ๋ฉ๋๋ค.
๋ณธ๋ฌธ ์ฝํ ์ธ ์ถ์ถํ๊ธฐ
์ค์ ๋ฉ์์ง ๋ณธ๋ฌธ์ ์ถ์ถํ๋ ค๋ฉด ๋ฉ์์ง๊ฐ ๋ฉํฐํํธ์ธ์ง ํ์ธํด์ผ ํฉ๋๋ค. ๋ฉํฐํํธ ๋ฉ์์ง์ ๊ฒฝ์ฐ, ํด๋น ํํธ๋ฅผ ์ํํฉ๋๋ค.
from email.message import EmailMessage
from email import message_from_string
multipart_email_raw = """
From: multi@example.com
To: user@example.com
Subject: Test Multipart Email
Content-Type: multipart/alternative; boundary="_----------=_12345"
--_----------=_12345
Content-Type: text/plain; charset="utf-8"
Hello from the plain text part!
--_----------=_12345
Content-Type: text/html; charset="utf-8"
<html>
<body>
<h1>Hello from the HTML part!</h1>
<p>This is a <strong>rich text</strong> email.</p>
</body>
</html>
--_----------=_12345--
"""
msg = message_from_string(multipart_email_raw)
if msg.is_multipart():
print("\n--- Multipart Email Body ---")
for part in msg.iter_parts():
content_type = part.get_content_type()
charset = part.get_content_charset() or 'utf-8' # ์ง์ ๋์ง ์์ ๊ฒฝ์ฐ utf-8๋ก ๊ธฐ๋ณธ๊ฐ ์ค์
payload = part.get_payload(decode=True) # ํ์ด๋ก๋ ๋ฐ์ดํธ ๋์ฝ๋ฉ
try:
decoded_content = payload.decode(charset)
print(f"Content-Type: {content_type}, Charset: {charset}\nContent:\n{decoded_content}\n")
except UnicodeDecodeError:
print(f"Content-Type: {content_type}, Charset: {charset}\nContent: (Binary or undecodable data)\n")
# ์ด์ง ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๊ฑฐ๋ ๋์ฒด ์ธ์ฝ๋ฉ ์๋
else:
print("\n--- Single Part Email Body ---")
charset = msg.get_content_charset() or 'utf-8'
payload = msg.get_payload(decode=True)
try:
decoded_content = payload.decode(charset)
print(f"Content-Type: {msg.get_content_type()}, Charset: {charset}\nContent:\n{decoded_content}\n")
except UnicodeDecodeError:
print(f"Content: (Binary or undecodable data)\n")
์ค๋ช :
is_multipart()
๋ ์ด๋ฉ์ผ์ด ์ฌ๋ฌ ํํธ๋ฅผ ๊ฐ์ง๊ณ ์๋์ง ํ์ธํฉ๋๋ค.iter_parts()
๋ ๋ฉํฐํํธ ๋ฉ์์ง์ ๋ชจ๋ ํ์ ํํธ๋ฅผ ์ํํฉ๋๋ค.get_content_type()
์ ์ ์ฒด MIME ์ ํ(์:text/plain
)์ ๋ฐํํฉ๋๋ค.get_content_charset()
์Content-Type
ํค๋์์ ๋ฌธ์ ์งํฉ์ ์ถ์ถํฉ๋๋ค.get_payload(decode=True)
๋ ๋งค์ฐ ์ค์ํฉ๋๋ค: ์ด๊ฒ์ *๋์ฝ๋ฉ๋* ์ฝํ ์ธ ๋ฅผ ๋ฐ์ดํธ๋ก ๋ฐํํฉ๋๋ค. ๊ทธ๋ฐ ๋ค์ ํ์ด์ฌ ๋ฌธ์์ด์ ์ป์ผ๋ ค๋ฉด ์ฌ๋ฐ๋ฅธ ๋ฌธ์ ์งํฉ์ ์ฌ์ฉํ์ฌ ์ด ๋ฐ์ดํธ๋ฅผ.decode()
ํด์ผ ํฉ๋๋ค.
ํ์ฑ ์ค ์ฒจ๋ถํ์ผ ์ฒ๋ฆฌํ๊ธฐ
์ฒจ๋ถํ์ผ๋ ๋ฉํฐํํธ ๋ฉ์์ง์ ์ผ๋ถ์
๋๋ค. Content-Disposition
ํค๋๋ฅผ ์ฌ์ฉํ์ฌ ์๋ณํ๊ณ ๋์ฝ๋ฉ๋ ํ์ด๋ก๋๋ฅผ ์ ์ฅํ ์ ์์ต๋๋ค.
from email.message import EmailMessage
from email import message_from_string
import os
# ๊ฐ๋จํ ์ฒจ๋ถํ์ผ์ด ์๋ ์ด๋ฉ์ผ ์์
email_with_attachment = """
From: attach@example.com
To: user@example.com
Subject: Document Attached
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="_----------=_XYZ"
--_----------=_XYZ
Content-Type: text/plain; charset="utf-8"
Here is your requested document.
--_----------=_XYZ
Content-Type: application/pdf
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="document.pdf"
JVBERi0xLjQKMSAwIG9iagpbL1BERi9UZXh0L0ltYWdlQy9JbWFnZUkvSW1hZ0VCXQplbmRvYmoK
--_----------=_XYZ--
"""
msg = message_from_string(email_with_attachment)
output_dir = 'parsed_attachments'
os.makedirs(output_dir, exist_ok=True)
print("\n--- Processing Attachments ---")
for part in msg.iter_attachments():
filename = part.get_filename()
if filename:
filepath = os.path.join(output_dir, filename)
try:
with open(filepath, 'wb') as f:
f.write(part.get_payload(decode=True))
print(f"Saved attachment: {filepath} (Type: {part.get_content_type()})")
except Exception as e:
print(f"Error saving {filename}: {e}")
else:
print(f"Found an attachment without a filename (Content-Type: {part.get_content_type()})")
# ์ถ๋ ฅ ๋๋ ํ ๋ฆฌ ์ ๋ฆฌ
# import shutil
# shutil.rmtree(output_dir)
์ค๋ช :
iter_attachments()
๋ ์ฒจ๋ถํ์ผ์ผ ๊ฐ๋ฅ์ฑ์ด ๋์ ํํธ(์ฆ,Content-Disposition: attachment
ํค๋๊ฐ ์๊ฑฐ๋ ๋ค๋ฅธ ๋ฐฉ์์ผ๋ก ๋ถ๋ฅ๋์ง ์์ ํํธ)๋ฅผ ํน๋ณํ ๋ฐํํฉ๋๋ค.get_filename()
์Content-Disposition
ํค๋์์ ํ์ผ๋ช ์ ์ถ์ถํฉ๋๋ค.part.get_payload(decode=True)
๋base64
๋๋quoted-printable
์์ ์ด๋ฏธ ๋์ฝ๋ฉ๋ ์ฒจ๋ถํ์ผ์ ์์ ์ด์ง ์ฝํ ์ธ ๋ฅผ ๊ฒ์ํฉ๋๋ค.
์ธ์ฝ๋ฉ ๋ฐ ๋ฌธ์ ์งํฉ ๋์ฝ๋ฉํ๊ธฐ
email
ํจํค์ง๋ get_payload(decode=True)
๋ฅผ ํธ์ถํ ๋ ์ผ๋ฐ์ ์ธ ์ ์ก ์ธ์ฝ๋ฉ(base64
, quoted-printable
๋ฑ)์ ์๋์ผ๋ก ๋์ฝ๋ฉํ๋ ํ๋ฅญํ ์์
์ ์ํํฉ๋๋ค. ํ
์คํธ ์ฝํ
์ธ ์์ฒด์ ๋ํด์๋ Content-Type
ํค๋์ ์ง์ ๋ charset
์ ์ฌ์ฉํ๋ ค๊ณ ์๋ํฉ๋๋ค. ๋ฌธ์ ์งํฉ์ด ์ง์ ๋์ง ์์๊ฑฐ๋ ์ ํจํ์ง ์์ ๊ฒฝ์ฐ, ์ฐ์ํ๊ฒ ์ฒ๋ฆฌํด์ผ ํ ์๋ ์์ต๋๋ค.
from email.message import EmailMessage
from email import message_from_string
# ๋ฌธ์ ๊ฐ ๋ ์ ์๋ ๋ฌธ์ ์งํฉ ์์
email_latin1 = """
From: legacy@example.com
To: new_system@example.com
Subject: Special characters: ร รฉรญรณรบ
Content-Type: text/plain; charset="iso-8859-1"
This message contains Latin-1 characters: ร รฉรญรณรบ
"""
msg = message_from_string(email_latin1)
if msg.is_multipart():
for part in msg.iter_parts():
payload = part.get_payload(decode=True)
charset = part.get_content_charset() or 'utf-8'
try:
print(f"Decoded (Charset: {charset}): {payload.decode(charset)}")
except UnicodeDecodeError:
print(f"Failed to decode with {charset}. Trying fallback...")
# ์ผ๋ฐ์ ์ธ ๋ฌธ์ ์งํฉ์ด๋ ์์๋๋ 'latin-1'์ผ๋ก ๋์ฒด
print(f"Decoded (Fallback Latin-1): {payload.decode('latin-1', errors='replace')}")
else:
payload = msg.get_payload(decode=True)
charset = msg.get_content_charset() or 'utf-8'
try:
print(f"Decoded (Charset: {charset}): {payload.decode(charset)}")
except UnicodeDecodeError:
print(f"Failed to decode with {charset}. Trying fallback...")
print(f"Decoded (Fallback Latin-1): {payload.decode('latin-1', errors='replace')}")
์ค๋ช :
- ํญ์
Content-Type
ํค๋์ ์ง์ ๋ ๋ฌธ์ ์งํฉ์ ์ฌ์ฉํ๋ ค๊ณ ์๋ํ์ธ์. - ๊ฒฌ๊ณ ์ฑ์ ์ํด, ํนํ ๋ค์ํ๊ณ ์ ์ฌ์ ์ผ๋ก ๋นํ์ค์ ์ธ ์์ค์์ ์จ ์ด๋ฉ์ผ์ ์ฒ๋ฆฌํ ๋
try-except UnicodeDecodeError
๋ธ๋ก์ ์ฌ์ฉํ์ธ์. .decode()
์ ํจ๊ปerrors='replace'
๋๋errors='ignore'
๋ฅผ ์ฌ์ฉํ์ฌ ๋์ ์ธ์ฝ๋ฉ์ผ๋ก ๋งคํํ ์ ์๋ ๋ฌธ์๋ฅผ ์ฒ๋ฆฌํ์ฌ ์ถฉ๋์ ๋ฐฉ์งํ ์ ์์ต๋๋ค.
๊ณ ๊ธ ํ์ฑ ์๋๋ฆฌ์ค
์ค์ ์ด๋ฉ์ผ์ ์ค์ฒฉ๋ ๋ฉํฐํํธ ๊ตฌ์กฐ๋ก ๋งค์ฐ ๋ณต์กํ ์ ์์ต๋๋ค. email
ํจํค์ง์ ์ฌ๊ท์ ํน์ฑ์ ์ด๋ฌํ ๊ตฌ์กฐ๋ฅผ ์ฝ๊ฒ ํ์ํ ์ ์๊ฒ ํด์ค๋๋ค. is_multipart()
์ iter_parts()
๋ฅผ ๊ฒฐํฉํ์ฌ ๊น์ด ์ค์ฒฉ๋ ๋ฉ์์ง๋ฅผ ์ํํ ์ ์์ต๋๋ค.
from email.message import EmailMessage
from email import message_from_string
def parse_email_part(part, indent=0):
prefix = " " * indent
content_type = part.get_content_type()
charset = part.get_content_charset() or 'N/A'
print(f"{prefix}Part Type: {content_type}, Charset: {charset}")
if part.is_multipart():
for subpart in part.iter_parts():
parse_email_part(subpart, indent + 1)
elif part.get_filename(): # ์ฒจ๋ถํ์ผ์ธ ๊ฒฝ์ฐ
print(f"{prefix} Attachment: {part.get_filename()} (Size: {len(part.get_payload(decode=True))} bytes)")
else: # ์ผ๋ฐ ํ
์คํธ/HTML ๋ณธ๋ฌธ ํํธ์ธ ๊ฒฝ์ฐ
payload = part.get_payload(decode=True)
try:
decoded_content = payload.decode(charset)
# print(f"{prefix} Content (first 100 chars): {decoded_content[:100]}...") # ๊ฐ๊ฒฐํจ์ ์ํด
except UnicodeDecodeError:
print(f"{prefix} Content: (Binary or undecodable text)")
complex_email_raw = """
From: complex@example.com
To: receiver@example.com
Subject: Complex Email with HTML, Plain, and Attachment
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="outer_boundary"
--outer_boundary
Content-Type: multipart/alternative; boundary="inner_boundary"
--inner_boundary
Content-Type: text/plain; charset="utf-8"
Plain text content.
--inner_boundary
Content-Type: text/html; charset="utf-8"
<html><body><h2>HTML Content</h2></body></html>
--inner_boundary--
--outer_boundary
Content-Type: application/octet-stream
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="data.bin"
SGVsbG8gV29ybGQh
--outer_boundary--
"""
msg = message_from_string(complex_email_raw)
print("\n--- Traversing Complex Email Structure ---")
parse_email_part(msg)
์ค๋ช :
- ์ฌ๊ท ํจ์
parse_email_part
๋ ์ ์ฒด ๋ฉ์์ง ํธ๋ฆฌ๋ฅผ ํ์ํ๋ฉด์ ๊ฐ ์์ค์์ ๋ฉํฐํํธ, ์ฒจ๋ถํ์ผ ๋ฐ ๋ณธ๋ฌธ ์ฝํ ์ธ ๋ฅผ ์๋ณํ๋ ๋ฐฉ๋ฒ์ ๋ณด์ฌ์ค๋๋ค. - ์ด ํจํด์ ๊น์ด ์ค์ฒฉ๋ ์ด๋ฉ์ผ์์ ํน์ ์ ํ์ ์ฝํ ์ธ ๋ฅผ ์ถ์ถํ๋ ๋ฐ ๋งค์ฐ ์ ์ฐํฉ๋๋ค.
๊ตฌ์ฑ vs. ํ์ฑ: ๋น๊ต ๊ด์
๋ณ๊ฐ์ ์์ ์ด์ง๋ง, ๊ตฌ์ฑ๊ณผ ํ์ฑ์ MIME ๋ฉ์์ง ์ฒ๋ฆฌ๋ผ๋ ๋์ ์ ์๋ฉด์ ๋๋ค. ํ๋๋ฅผ ์ดํดํ๋ฉด ํ์ฐ์ ์ผ๋ก ๋ค๋ฅธ ํ๋๋ฅผ ์ดํดํ๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค.
๊ตฌ์ฑ (๋ฐ์ก):
- ์ด์ : ํค๋, ์ฝํ ์ธ ๋ฐ ์ฒจ๋ถํ์ผ์ ํ์ค์ ์ค์ํ๋ MIME ๊ตฌ์กฐ๋ก ์ฌ๋ฐ๋ฅด๊ฒ ์กฐ๋ฆฝํ๋ ๊ฒ.
- ์ฃผ์ ๋๊ตฌ:
set_content()
,add_attachment()
,add_alternative()
,add_related()
์ ๊ฐ์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋email.message.EmailMessage
. - ์ฃผ์ ๊ณผ์ : ์ฌ๋ฐ๋ฅธ MIME ์ ํ, ๋ฌธ์ ์งํฉ(ํนํ ๊ธ๋ก๋ฒ ์ง์์ ์ํ UTF-8), `Content-Transfer-Encoding` ๋ฐ ์ ์ ํ ํค๋ ํ์์ ๋ณด์ฅํ๋ ๊ฒ. ์ค์๋ ์ด๋ฉ์ผ์ด ์ฌ๋ฐ๋ฅด๊ฒ ํ์๋์ง ์๊ฑฐ๋, ์ฒจ๋ถํ์ผ์ด ์์๋๊ฑฐ๋, ๋ฉ์์ง๊ฐ ์คํธ์ผ๋ก ๋ถ๋ฅ๋๋ ๊ฒฐ๊ณผ๋ฅผ ์ด๋ํ ์ ์์ต๋๋ค.
ํ์ฑ (์์ ):
- ์ด์ : ์์ ์ด๋ฉ์ผ ๋ฐ์ดํธ ์คํธ๋ฆผ์ ๊ตฌ์ฑ ์์๋ก ๋ถํดํ๊ณ ํน์ ํค๋, ๋ณธ๋ฌธ ์ฝํ ์ธ ๋ฐ ์ฒจ๋ถํ์ผ์ ์ถ์ถํ๋ ๊ฒ.
- ์ฃผ์ ๋๊ตฌ:
email.parser.BytesParser
๋๋email.message_from_bytes()
๋ฅผ ์ฌ์ฉํ ํ,is_multipart()
,iter_parts()
,get_payload()
,get_filename()
๋ฐ ํค๋ ์ ๊ทผ๊ณผ ๊ฐ์ ๋ฉ์๋๋ก ๊ฒฐ๊ณผEmailMessage
๊ฐ์ฒด๋ฅผ ํ์ํ๋ ๊ฒ. - ์ฃผ์ ๊ณผ์ : ์๋ชป๋ ํ์์ ์ด๋ฉ์ผ ์ฒ๋ฆฌ, ๋ฌธ์ ์ธ์ฝ๋ฉ์ ์ฌ๋ฐ๋ฅด๊ฒ ์๋ณ(ํนํ ๋ชจํธํ ๋), ๋๋ฝ๋ ํค๋ ์ฒ๋ฆฌ, ๋ค์ํ MIME ๊ตฌ์กฐ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฒฌ๊ณ ํ๊ฒ ์ถ์ถํ๋ ๊ฒ.
`EmailMessage`๋ฅผ ์ฌ์ฉํ์ฌ ๊ตฌ์ฑํ ๋ฉ์์ง๋ `BytesParser`์ ์ํด ์๋ฒฝํ๊ฒ ํ์ฑ๋ ์ ์์ด์ผ ํฉ๋๋ค. ๋ง์ฐฌ๊ฐ์ง๋ก, ํ์ฑ ์ค์ ์์ฑ๋ MIME ๊ตฌ์กฐ๋ฅผ ์ดํดํ๋ฉด ๋ณต์กํ ๋ฉ์์ง๋ฅผ ์ง์ ๋ง๋๋ ๋ฐฉ๋ฒ์ ๋ํ ํต์ฐฐ๋ ฅ์ ์ป์ ์ ์์ต๋๋ค.
ํ์ด์ฌ์ผ๋ก ๊ธ๋ก๋ฒ ์ด๋ฉ์ผ์ ์ฒ๋ฆฌํ๊ธฐ ์ํ ๋ชจ๋ฒ ์ฌ๋ก
๊ธ๋ก๋ฒ ๊ณ ๊ฐ๊ณผ ์ํธ ์์ฉํ๊ฑฐ๋ ๋ค์ํ ์ด๋ฉ์ผ ์์ค๋ฅผ ์ฒ๋ฆฌํ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฒฝ์ฐ ๋ค์ ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ๊ณ ๋ คํ์ญ์์ค:
- UTF-8 ํ์คํ: ๊ตฌ์ฑํ ๋์ ํ์ฑํ ๋ ๋ชจ๋ ๋ชจ๋ ํ ์คํธ ์ฝํ ์ธ ์ ํญ์ UTF-8์ ์ฌ์ฉํ์ธ์. ์ด๋ ๋ฌธ์ ์ธ์ฝ๋ฉ์ ๊ธ๋ก๋ฒ ํ์ค์ด๋ฉฐ ๋ชจ์ง๋ฐ์ผ(๊นจ์ง ํ ์คํธ)๋ฅผ ๋ฐฉ์งํฉ๋๋ค.
- ์ด๋ฉ์ผ ์ฃผ์ ์ ํจ์ฑ ๊ฒ์ฌ: ๋ณด๋ด๊ธฐ ์ ์ ์์ ์ ์ด๋ฉ์ผ ์ฃผ์์ ์ ํจ์ฑ์ ๊ฒ์ฌํ์ฌ ์ ๋ฌ ๊ฐ๋ฅ์ฑ์ ๋ณด์ฅํ์ธ์. ํ์ฑํ ๋๋ `From`, `To`, `Cc` ํค๋์ ์ ์ฌ์ ์ผ๋ก ์ ํจํ์ง ์๊ฑฐ๋ ์๋ชป๋ ํ์์ ์ฃผ์๊ฐ ์์ ์ ์์์ ๋๋นํ์ธ์.
- ์๊ฒฉํ ํ ์คํธ: ๋ค์ํ ์ด๋ฉ์ผ ํด๋ผ์ด์ธํธ(Gmail, Outlook, Apple Mail, Thunderbird) ๋ฐ ํ๋ซํผ์์ ์ด๋ฉ์ผ ๊ตฌ์ฑ์ ํ ์คํธํ์ฌ HTML ๋ฐ ์ฒจ๋ถํ์ผ์ ์ผ๊ด๋ ๋ ๋๋ง์ ๋ณด์ฅํ์ธ์. ํ์ฑ์ ๊ฒฝ์ฐ, ํน์ดํ ์ธ์ฝ๋ฉ, ๋๋ฝ๋ ํค๋ ๋๋ ๋ณต์กํ ์ค์ฒฉ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง ๋ค์ํ ์ํ ์ด๋ฉ์ผ๋ก ํ ์คํธํ์ธ์.
- ํ์ฑ๋ ์ ๋ ฅ ์๋ : ์์ ์ด๋ฉ์ผ์์ ์ถ์ถํ ์ฝํ ์ธ ๋ ํญ์ ์ ๋ขฐํ ์ ์๋ ๊ฒ์ผ๋ก ์ทจ๊ธํ์ธ์. ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ํ์ํ๋ ๊ฒฝ์ฐ XSS ๊ณต๊ฒฉ์ ๋ฐฉ์งํ๊ธฐ ์ํด HTML ์ฝํ ์ธ ๋ฅผ ์๋ ํ์ธ์. ํ์ผ์ ์ ์ฅํ ๋ ๊ฒฝ๋ก ํ์์ด๋ ๊ธฐํ ๋ณด์ ์ทจ์ฝ์ ์ ๋ฐฉ์งํ๊ธฐ ์ํด ์ฒจ๋ถํ์ผ ์ด๋ฆ๊ณผ ์ ํ์ ์ ํจ์ฑ์ ๊ฒ์ฌํ์ธ์.
- ๊ฒฌ๊ณ ํ ์ค๋ฅ ์ฒ๋ฆฌ: ํ์ด๋ก๋๋ฅผ ๋์ฝ๋ฉํ๊ฑฐ๋ ์ ์ฌ์ ์ผ๋ก ๋๋ฝ๋ ํค๋์ ์ ๊ทผํ ๋ ํฌ๊ด์ ์ธ
try-except
๋ธ๋ก์ ๊ตฌํํ์ธ์.UnicodeDecodeError
๋๋KeyError
๋ฅผ ์ฐ์ํ๊ฒ ์ฒ๋ฆฌํ์ธ์. - ๋์ฉ๋ ์ฒจ๋ถํ์ผ ์ฒ๋ฆฌ: ๊ตฌ์ฑํ ๋(๋ฉ์ผ ์๋ฒ ์ ํ์ ์ด๊ณผํ์ง ์๋๋ก)์ ํ์ฑํ ๋(๊ณผ๋ํ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ์ด๋ ๋์คํฌ ๊ณต๊ฐ ์๋น๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด) ์ฒจ๋ถํ์ผ ํฌ๊ธฐ์ ์ ์ํ์ธ์. ์์คํ ์ด ์ง์ํ๋ ๊ฒฝ์ฐ ๋์ฉ๋ ์ฒจ๋ถํ์ผ ์คํธ๋ฆฌ๋ฐ์ ๊ณ ๋ คํ์ธ์.
email.policy
ํ์ฉ: ์ค์ํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฒฝ์ฐ, ์ ๋ฌ ๊ฐ๋ฅ์ฑ ๋ฐ ์ํธ ์ด์ฉ์ฑ์ ์ํฅ์ ๋ฏธ์น ์ ์๋ ์ด๋ฉ์ผ ํ์ค์ ์๊ฒฉํ๊ฒ ์ค์ํ๋๋ก `email.policy`(์: `policy.SMTP`)๋ฅผ ๋ช ์์ ์ผ๋ก ์ ํํ์ธ์.- ๋ฉํ๋ฐ์ดํฐ ๋ณด์กด: ํ์ฑํ ๋, ํนํ ๋ฉ์ผ ๋ณด๊ด ๋๋ ์ ๋ฌ ์์คํ ์ ๊ตฌ์ถํ๋ ๊ฒฝ์ฐ ์ด๋ค ๋ฉํ๋ฐ์ดํฐ(ํค๋, ์๋ณธ ๊ฒฝ๊ณ ๋ฌธ์์ด)๋ฅผ ๋ณด์กดํ๋ ๊ฒ์ด ์ค์ํ์ง ๊ฒฐ์ ํ์ธ์.
๊ฒฐ๋ก
ํ์ด์ฌ์ email
ํจํค์ง๋ ํ๋ก๊ทธ๋๋ฐ ๋ฐฉ์์ผ๋ก ์ด๋ฉ์ผ๊ณผ ์ํธ ์์ฉํด์ผ ํ๋ ๋ชจ๋ ์ฌ๋์๊ฒ ๋งค์ฐ ๊ฐ๋ ฅํ๊ณ ์ ์ฐํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์
๋๋ค. MIME ๋ฉ์์ง ๊ตฌ์ฑ๊ณผ ์์ ์ด๋ฉ์ผ์ ๊ฒฌ๊ณ ํ ํ์ฑ์ ๋ชจ๋ ๋ง์คํฐํจ์ผ๋ก์จ ์ ๊ตํ ์ด๋ฉ์ผ ์๋ํ ์์คํ
์ ๋ง๋ค๊ณ , ์ด๋ฉ์ผ ํด๋ผ์ด์ธํธ๋ฅผ ๊ตฌ์ถํ๊ณ , ์ด๋ฉ์ผ ๋ฐ์ดํฐ๋ฅผ ๋ถ์ํ๊ณ , ์ฌ์ค์ ๋ชจ๋ ์ ํ๋ฆฌ์ผ์ด์
์ ์ด๋ฉ์ผ ๊ธฐ๋ฅ์ ํตํฉํ ์ ์๋ ๋ฅ๋ ฅ์ ๊ฐ์ถ๊ฒ ๋ฉ๋๋ค.
์ด ํจํค์ง๋ MIME์ ๊ทผ๋ณธ์ ์ธ ๋ณต์ก์ฑ์ ์ฌ๋ ค ๊น๊ฒ ์ฒ๋ฆฌํ์ฌ ๊ฐ๋ฐ์๊ฐ ์ด๋ฉ์ผ ์ํธ ์์ฉ์ ์ฝํ
์ธ ์ ๋ก์ง์ ์ง์คํ ์ ์๋๋ก ํฉ๋๋ค. ๊ธ๋ก๋ฒ ๊ณ ๊ฐ์๊ฒ ๊ฐ์ธํ๋ ๋ด์ค๋ ํฐ๋ฅผ ๋ณด๋ด๋ , ์๋ํ๋ ์์คํ
๋ณด๊ณ ์์์ ์ค์ํ ๋ฐ์ดํฐ๋ฅผ ์ถ์ถํ๋ , email
ํจํค์ง์ ๋ํ ๊น์ ์ดํด๋ ์ ๋ขฐํ ์ ์๊ณ ์ํธ ์ด์ฉ ๊ฐ๋ฅํ๋ฉฐ ๊ธ๋ก๋ฒ ์ธ์์ ๊ฐ์ถ ์ด๋ฉ์ผ ์๋ฃจ์
์ ๊ตฌ์ถํ๋ ๋ฐ ๋งค์ฐ ๊ท์คํ ์์ฐ์ด ๋ ๊ฒ์
๋๋ค.